using System;
using System.Diagnostics;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    using sqlite3_value = Mem;

    public  class Alter
    {
        /*
    ** 2005 February 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains C code routines that used to generate VDBE code
    ** that implements the ALTER TABLE command.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/alter_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
        //#include "sqliteInt.h"

        /*
    ** The code in this file only exists if we are not omitting the
    ** ALTER TABLE logic from the build.
    */
#if !SQLITE_OMIT_ALTERTABLE


        /*
** This function is used by SQL generated to implement the
** ALTER TABLE command. The first argument is the text of a CREATE TABLE or
** CREATE INDEX command. The second is a table name. The table name in
** the CREATE TABLE or CREATE INDEX statement is replaced with the third
** argument and the result returned. Examples:
**
** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def')
**     . 'CREATE TABLE def(a, b, c)'
**
** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def')
**     . 'CREATE INDEX i ON def(a, b, c)'
*/

        private static void renameTableFunc(
            sqlite3_context context,
            int NotUsed,
            sqlite3_value[] argv
            )
        {
            string bResult = Sqlite3.sqlite3_value_text(argv[0]);
            string zSql = bResult == null ? "" : bResult;
            string zTableName = Sqlite3.sqlite3_value_text(argv[1]);

            int token = 0;
            var tname = new Token();
            int zCsr = 0;
            int zLoc = 0;
            int len = 0;
            string zRet;

            sqlite3 db =Sqlite3. sqlite3_context_db_handle(context);

            Helper.UNUSED_PARAMETER(NotUsed);

            /* The principle used to locate the table name in the CREATE TABLE
      ** statement is that the table name is the first non-space token that
      ** is immediately followed by a TokenKeyword.TK_LP or TokenKeyword.TK_USING token.
      */
            if (zSql != "")
            {
                do
                {
                    if (zCsr == zSql.Length)
                    {
                        /* Ran out of input before finding an opening bracket. Return NULL. */
                        return;
                    }

                    /* Store the token that zCsr points to in tname. */
                    zLoc = zCsr;
                    tname.z = zSql.Substring(zCsr); //(char*)zCsr;
                    tname.n = len;

                    /* Advance zCsr to the next token. Store that token type in 'token',
          ** and its length in 'len' (to be used next iteration of this loop).
          */
                    do
                    {
                        zCsr += len;
                        len = (zCsr == zSql.Length) ? 1 : Tokenize.GetToken(zSql, zCsr, ref token);
                    } while (token == TokenKeyword.TK_SPACE);
                    Debug.Assert(len > 0);
                } while (token != TokenKeyword.TK_LP && token != TokenKeyword.TK_USING);

                zRet = Print.MPrintf(db, "%.*s\"%w\"%s", zLoc, zSql.Substring(0, zLoc),
                                      zTableName, zSql.Substring(zLoc + tname.n));

                Sqlite3.sqlite3_result_text(context, zRet, -1, Const.SQLITE_DYNAMIC);
            }
        }

        /*
    ** This C function implements an SQL user function that is used by SQL code
    ** generated by the ALTER TABLE ... RENAME command to modify the definition
    ** of any foreign key constraints that use the table being renamed as the 
    ** parent table. It is passed three arguments:
    **
    **   1) The complete text of the CREATE TABLE statement being modified,
    **   2) The old name of the table being renamed, and
    **   3) The new name of the table being renamed.
    **
    ** It returns the new CREATE TABLE statement. For example:
    **
    **   sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
    **       -> 'CREATE TABLE t1(a REFERENCES t3)'
    */
#if !SQLITE_OMIT_FOREIGN_KEY
        private static void renameParentFunc(
            sqlite3_context context,
            int NotUsed,
            sqlite3_value[] argv
            )
        {
            sqlite3 db = Sqlite3.sqlite3_context_db_handle(context);
            string zOutput = "";
            string zResult;
            string zInput = Sqlite3.sqlite3_value_text(argv[0]);
            string zOld = Sqlite3.sqlite3_value_text(argv[1]);
            string zNew = Sqlite3.sqlite3_value_text(argv[2]);

            int zIdx; /* Pointer to token */
            int zLeft = 0; /* Pointer to remainder of String */
            int n = 0; /* Length of token z */
            int token = 0; /* Type of token */

            Helper.UNUSED_PARAMETER(NotUsed);
            for (zIdx = 0; zIdx < zInput.Length; zIdx += n) //z=zInput; *z; z=z+n)
            {
                n = Tokenize.GetToken(zInput, zIdx, ref token);
                if (token == TokenKeyword.TK_REFERENCES)
                {
                    string zParent;
                    do
                    {
                        zIdx += n;
                        n = Tokenize.GetToken(zInput, zIdx, ref token);
                    } while (token == TokenKeyword.TK_SPACE);

                    zParent = zIdx + n < zInput.Length ? zInput.Substring(zIdx, n) : "";
                        //sqlite3DbStrNDup(db, zIdx, n);
                    if (String.IsNullOrEmpty(zParent)) break;
                    Utility.Sqlite3Dequote(ref zParent);
                    if (0 == Utility.Sqlite3StrICmp(zOld, zParent))
                    {
                        string zOut = Print.MPrintf(db, "%s%.*s\"%w\"",
                                                     zOutput, zIdx - zLeft, zInput.Substring(zLeft), zNew
                            );
                        MemPool.DbFree(db, ref zOutput);
                        zOutput = zOut;
                        zIdx += n; // zInput = &z[n];
                        zLeft = zIdx;
                    }
                    MemPool.DbFree(db, ref zParent);
                }
            }

            zResult = Print.MPrintf(db, "%s%s", zOutput, zInput.Substring(zLeft));
            Sqlite3.sqlite3_result_text(context, zResult, -1, Const.SQLITE_DYNAMIC);
            MemPool.DbFree(db, ref zOutput);
        }
#endif

#if !SQLITE_OMIT_TRIGGER
        /* This function is used by SQL generated to implement the
** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER
** statement. The second is a table name. The table name in the CREATE
** TRIGGER statement is replaced with the third argument and the result
** returned. This is analagous to renameTableFunc() above, except for CREATE
** TRIGGER, not CREATE INDEX and CREATE TABLE.
*/

        private static void renameTriggerFunc(
            sqlite3_context context,
            int NotUsed,
            sqlite3_value[] argv
            )
        {
            string zSql = Sqlite3.sqlite3_value_text(argv[0]);
            string zTableName = Sqlite3.sqlite3_value_text(argv[1]);

            int token = 0;
            var tname = new Token();
            int dist = 3;
            int zCsr = 0;
            int zLoc = 0;
            int len = 1;
            string zRet;

            sqlite3 db = Sqlite3.sqlite3_context_db_handle(context);

            Helper.UNUSED_PARAMETER(NotUsed);

            /* The principle used to locate the table name in the CREATE TRIGGER
      ** statement is that the table name is the first token that is immediatedly
      ** preceded by either TokenKeyword.TK_ON or TokenKeyword.TK_DOT and immediatedly followed by one
      ** of TokenKeyword.TK_WHEN, TokenKeyword.TK_BEGIN or TokenKeyword.TK_FOR.
      */
            if (zSql != null)
            {
                do
                {
                    if (zCsr == zSql.Length)
                    {
                        /* Ran out of input before finding the table name. Return NULL. */
                        return;
                    }

                    /* Store the token that zCsr points to in tname. */
                    zLoc = zCsr;
                    tname.z = zSql.Substring(zCsr, len); //(char*)zCsr;
                    tname.n = len;

                    /* Advance zCsr to the next token. Store that token type in 'token',
          ** and its length in 'len' (to be used next iteration of this loop).
          */
                    do
                    {
                        zCsr += len;
                        len = (zCsr == zSql.Length) ? 1 : Tokenize.GetToken(zSql, zCsr, ref token);
                    } while (token == TokenKeyword.TK_SPACE);
                    Debug.Assert(len > 0);

                    /* Variable 'dist' stores the number of tokens read since the most
          ** recent TokenKeyword.TK_DOT or TokenKeyword.TK_ON. This means that when a WHEN, FOR or BEGIN
          ** token is read and 'dist' equals 2, the condition stated above
          ** to be met.
          **
          ** Note that ON cannot be a database, table or column name, so
          ** there is no need to worry about syntax like
          ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc.
          */
                    dist++;
                    if (token == TokenKeyword.TK_DOT || token == TokenKeyword.TK_ON)
                    {
                        dist = 0;
                    }
                } while (dist != 2 || (token != TokenKeyword.TK_WHEN && token != TokenKeyword.TK_FOR && token != TokenKeyword.TK_BEGIN));

                /* Variable tname now contains the token that is the old table-name
        ** in the CREATE TRIGGER statement.
        */
                zRet = Print.MPrintf(db, "%.*s\"%w\"%s", zLoc, zSql.Substring(0, zLoc),
                                      zTableName, zSql.Substring(zLoc + tname.n));
                Sqlite3.sqlite3_result_text(context, zRet, -1, Const.SQLITE_DYNAMIC);
            }
        }
#endif
        // * !SQLITE_OMIT_TRIGGER */

        /*
** Register built-in functions used to help implement ALTER TABLE
*/

        public static void AlterFunctions(sqlite3 db)
        {
            sqlite3CreateFunc(db, "sqlite_rename_table", 2, Const.SQLITE_UTF8, 0,
                              renameTableFunc, null, null);
#if !SQLITE_OMIT_TRIGGER
            sqlite3CreateFunc(db, "sqlite_rename_trigger", 2, Const.SQLITE_UTF8, 0,
                              renameTriggerFunc, null, null);
#endif
#if !SQLITE_OMIT_FOREIGN_KEY
            sqlite3CreateFunc(db, "sqlite_rename_parent", 3, Const.SQLITE_UTF8, 0,
                              renameParentFunc, null, null);
#endif
        }

        /*
    ** This function is used to create the text of expressions of the form:
    **
    **   name=<constant1> OR name=<constant2> OR ...
    **
    ** If argument zWhere is NULL, then a pointer string containing the text 
    ** "name=<constant>" is returned, where <constant> is the quoted version
    ** of the string passed as argument zConstant. The returned buffer is
    ** allocated using sqlite3DbMalloc(). It is the responsibility of the
    ** caller to ensure that it is eventually freed.
    **
    ** If argument zWhere is not NULL, then the string returned is 
    ** "<where> OR name=<constant>", where <where> is the contents of zWhere.
    ** In this case zWhere is passed to MemPool.DbFree() before returning.
    ** 
    */

        private static string whereOrName(sqlite3 db, string zWhere, string zConstant)
        {
            string zNew;
            if (String.IsNullOrEmpty(zWhere))
            {
                zNew = Print.MPrintf(db, "name=%Q", zConstant);
            }
            else
            {
                zNew = Print.MPrintf(db, "%s OR name=%Q", zWhere, zConstant);
                MemPool.DbFree(db, ref zWhere);
            }
            return zNew;
        }

#if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER)
/*
** Generate the text of a WHERE expression which can be used to select all
** tables that have foreign key constraints that refer to table pTab (i.e.
** constraints for which pTab is the parent table) from the sqlite_master
** table.
*/

        private static string whereForeignKeys(Parse pParse, Table pTab)
        {
            FKey p;
            string zWhere = "";
            for (p = sqlite3FkReferences(pTab); p != null; p = p.pNextTo)
            {
                zWhere = whereOrName(pParse.db, zWhere, p.pFrom.zName);
            }
            return zWhere;
        }
#endif

        /*
    ** Generate the text of a WHERE expression which can be used to select all
    ** temporary triggers on table pTab from the sqlite_temp_master table. If
    ** table pTab has no temporary triggers, or is itself stored in the
    ** temporary database, NULL is returned.
    */

        private static string whereTempTriggers(Parse pParse, Table pTab)
        {
            Trigger pTrig;
            string zWhere = "";
            Schema pTempSchema = pParse.db.aDb[1].pSchema; /* Temp db schema */

            /* If the table is not located in the temp.db (in which case NULL is
      ** returned, loop through the tables list of triggers. For each trigger
      ** that is not part of the temp.db schema, add a clause to the WHERE
      ** expression being built up in zWhere.
      */
            if (pTab.pSchema != pTempSchema)
            {
                sqlite3 db = pParse.db;
                for (pTrig = sqlite3TriggerList(pParse, pTab); pTrig != null; pTrig = pTrig.pNext)
                {
                    if (pTrig.pSchema == pTempSchema)
                    {
                        zWhere = whereOrName(db, zWhere, pTrig.zName);
                    }
                }
            }
            return zWhere;
        }

        /*
    ** Generate code to drop and reload the internal representation of table
    ** pTab from the database, including triggers and temporary triggers.
    ** Argument zName is the name of the table in the database schema at
    ** the time the generated code is executed. This can be different from
    ** pTab.zName if this function is being called to code part of an
    ** "ALTER TABLE RENAME TO" statement.
    */

        private static void reloadTableSchema(Parse pParse, Table pTab, string zName)
        {
            Vdbe v;
            string zWhere;
            int iDb; /* Index of database containing pTab */
#if !SQLITE_OMIT_TRIGGER
            Trigger pTrig;
#endif

            v = SelectHelper.GetVdbe(pParse);
            if (UnitTest.NEVER(v == null)) return;
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(pParse.db));
            iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);
            Debug.Assert(iDb >= 0);

#if !SQLITE_OMIT_TRIGGER
            /* Drop any table triggers from the internal schema. */
            for (pTrig = sqlite3TriggerList(pParse, pTab); pTrig != null; pTrig = pTrig.pNext)
            {
                int iTrigDb = sqlite3SchemaToIndex(pParse.db, pTrig.pSchema);
                Debug.Assert(iTrigDb == iDb || iTrigDb == 1);
                VdbeAux.VdbeAddOp4(v, OPCode.OP_DropTrigger, iTrigDb, 0, 0, pTrig.zName, 0);
            }
#endif

            /* Drop the table and index from the internal schema. */
            VdbeAux.VdbeAddOp4(v, OPCode.OP_DropTable, iDb, 0, 0, pTab.zName, 0);

            /* Reload the table, index and permanent trigger schemas. */
            zWhere = Print.MPrintf(pParse.db, "tbl_name=%Q", zName);
            if (zWhere == null) return;
            VdbeAux.VdbeAddOp4(v, OPCode.OP_ParseSchema, iDb, 0, 0, zWhere, P4Type.P4_DYNAMIC);

#if !SQLITE_OMIT_TRIGGER
            /* Now, if the table is not stored in the temp database, reload any temp
** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
*/
            if ((zWhere = whereTempTriggers(pParse, pTab)) != "")
            {
                VdbeAux.VdbeAddOp4(v, OPCode.OP_ParseSchema, 1, 0, 0, zWhere, P4Type.P4_DYNAMIC);
            }
#endif
        }

        /*
    ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
    ** command.
    */

        public static void AlterRenameTable(
            Parse pParse, /* Parser context. */
            SrcList pSrc, /* The table to rename. */
            Token pName /* The new table name. */
            )
        {
            int iDb; /* Database that contains the table */
            string zDb; /* Name of database iDb */
            Table pTab; /* Table being renamed */
            string zName = null; /* NULL-terminated version of pName */
            sqlite3 db = pParse.db; /* Database connection */
            int nTabName; /* Number of UTF-8 characters in zTabName */
            string zTabName; /* Original name of the table */
            Vdbe v;
#if !SQLITE_OMIT_TRIGGER
            string zWhere = ""; /* Where clause to locate temp triggers */
#endif
            VTable pVTab = null; /* Non-zero if this is a v-tab with an xRename() */

            //if ( UnitTest.NEVER( db.mallocFailed != 0 ) ) goto exit_rename_table;
            Debug.Assert(pSrc.nSrc == 1);
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(pParse.db));
            pTab = Build.LocateTable(pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase);
            if (pTab == null) goto exit_rename_table;
            iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);
            zDb = db.aDb[iDb].zName;

            /* Get a NULL terminated version of the new table name. */
            zName = Build.NameFromToken(db, pName);
            if (zName == null) goto exit_rename_table;

            /* Check that a table or index named 'zName' does not already exist
      ** in database iDb. If so, this is an error.
      */
            if (Build.FindTable(db, zName, zDb) != null || Build.FindIndex(db, zName, zDb) != null)
            {
                Utility.Sqlite3ErrorMsg(pParse,
                                "there is already another table or index with this name: %s", zName);
                goto exit_rename_table;
            }

            /* Make sure it is not a system table being altered, or a reserved name
      ** that the table is being renamed to.
      */
            if (Utility.Sqlite3Strlen30(pTab.zName) > 6
                && 0 == Utility.Sqlite3StrNICmp(pTab.zName, "sqlite_", 7)
                )
            {
                Utility.Sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab.zName);
                goto exit_rename_table;
            }
            if (StatusCode.SQLITE_OK != Build.CheckObjectName(pParse, zName))
            {
                goto exit_rename_table;
            }

#if !SQLITE_OMIT_VIEW
            if (pTab.pSelect != null)
            {
                Utility.Sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab.zName);
                goto exit_rename_table;
            }
#endif

#if !SQLITE_OMIT_AUTHORIZATION
/* Invoke the authorization callback. */
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_ALTER_TABLE, zDb, pTab.zName, 0) ){
goto exit_rename_table;
}
#endif

            if (Build.ViewGetColumnNames(pParse, pTab) != 0)
            {
                goto exit_rename_table;
            }
#if !SQLITE_OMIT_VIRTUALTABLE
  if( Utility.IsVirtual(pTab) ){
    pVTab = sqlite3GetVTable(db, pTab);
    if( pVTab.pVtab.pModule.xRename==null ){
      pVTab = null;
    }
#endif
            /* Begin a transaction and code the VerifyCookie for database iDb.
** Then modify the schema cookie (since the ALTER TABLE modifies the
** schema). Open a statement transaction if the table is a virtual
** table.
*/
            v = SelectHelper.GetVdbe(pParse);
            if (v == null)
            {
                goto exit_rename_table;
            }
            Build.BeginWriteOperation(pParse, pVTab != null ? 1 : 0, iDb);
            Build.ChangeCookie(pParse, iDb);

            /* If this is a virtual table, invoke the xRename() function if
      ** one is defined. The xRename() callback will modify the names
      ** of any resources used by the v-table implementation (including other
      ** SQLite tables) that are identified by the name of the virtual table.
      */
#if  !SQLITE_OMIT_VIRTUALTABLE
if ( pVTab !=null)
{
int i = ++pParse.nMem;
VdbeAux.VdbeAddOp4( v, OPCode.OP_String8, 0, i, 0, zName, 0 );
VdbeAux.VdbeAddOp4( v, OPCode.OP_VRename, i, 0, 0, pVtab, P4Type.P4_VTAB );
Build.MayAbort(pParse);
}
#endif

            /* figure out how many UTF-8 characters are in zName */
            zTabName = pTab.zName;
            nTabName = UtfHelper.Utf8CharLen(zTabName, -1);

#if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER)
            if ((db.flags & Flag.SQLITE_ForeignKeys) != 0)
            {
                /* If foreign-key support is enabled, rewrite the CREATE TABLE 
    ** statements corresponding to all child tables of foreign key constraints
    ** for which the renamed table is the parent table.  */
                if ((zWhere = whereForeignKeys(pParse, pTab)) != null)
                {
                    Build.NestedParse(pParse,
                                       "UPDATE \"%w\".%s SET " +
                                       "sql = sqlite_rename_parent(sql, %Q, %Q) " +
                                       "WHERE %s;", zDb, Helper.SchemaTable(iDb), zTabName, zName, zWhere);
                    MemPool.DbFree(db, ref zWhere);
                }
            }
#endif

            /* Modify the sqlite_master table to use the new table name. */
            Build.NestedParse(pParse,
                               "UPDATE %Q.%s SET " +
#if SQLITE_OMIT_TRIGGER
 "sql = sqlite_rename_table(sql, %Q), " +
#else
                               "sql = CASE " +
                               "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" +
                               "ELSE sqlite_rename_table(sql, %Q) END, " +
#endif
                               "tbl_name = %Q, " +
                               "name = CASE " +
                               "WHEN type='table' THEN %Q " +
                               "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " +
                               "'sqlite_autoindex_' || %Q || substr(name,%d+18) " +
                               "ELSE name END " +
                               "WHERE tbl_name=%Q AND " +
                               "(type='table' OR type='index' OR type='trigger');",
                               zDb, Helper.SchemaTable(iDb), zName, zName, zName,
#if !SQLITE_OMIT_TRIGGER
                               zName,
#endif
                               zName, nTabName, zTabName
                );

#if !SQLITE_OMIT_AUTOINCREMENT
            /* If the sqlite_sequence table exists in this database, then update
** it with the new table name.
*/
            if (Build.FindTable(db, "sqlite_sequence", zDb) != null)
            {
                Build.NestedParse(pParse,
                                   "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q",
                                   zDb, zName, pTab.zName
                    );
            }
#endif

#if !SQLITE_OMIT_TRIGGER
            /* If there are TEMP triggers on this table, modify the sqlite_temp_master
** table. Don't do this if the table being ALTERed is itself located in
** the temp database.
*/
            if ((zWhere = whereTempTriggers(pParse, pTab)) != "")
            {
                Build.NestedParse(pParse,
                                   "UPDATE sqlite_temp_master SET " +
                                   "sql = sqlite_rename_trigger(sql, %Q), " +
                                   "tbl_name = %Q " +
                                   "WHERE %s;", zName, zName, zWhere);
                MemPool.DbFree(db, ref zWhere);
            }
#endif

#if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER)
            if ((db.flags & Flag.SQLITE_ForeignKeys) != 0)
            {
                FKey p;
                for (p = sqlite3FkReferences(pTab); p != null; p = p.pNextTo)
                {
                    Table pFrom = p.pFrom;
                    if (pFrom != pTab)
                    {
                        reloadTableSchema(pParse, p.pFrom, pFrom.zName);
                    }
                }
            }
#endif

            /* Drop and reload the internal table schema. */
            reloadTableSchema(pParse, pTab, zName);

            exit_rename_table:
            Build.SrcListDelete(db, ref pSrc);
            MemPool.DbFree(db, ref zName);
        }

        /*
    ** Generate code to make sure the file format number is at least minFormat.
    ** The generated code will increase the file format number if necessary.
    */

        public static void MinimumFileFormat(Parse pParse, int iDb, int minFormat)
        {
            Vdbe v;
            v = SelectHelper.GetVdbe(pParse);
            /* The VDBE should have been allocated before this routine is called.
      ** If that allocation failed, we would have quit before reaching this
      ** point */
            if (UnitTest.ALWAYS(v))
            {
                int r1 = ExprHelper.GetTempReg(pParse);
                int r2 = ExprHelper.GetTempReg(pParse);
                int j1;
                VdbeAux.VdbeAddOp3(v, OPCode.OP_ReadCookie, iDb, r1, BtreeMeta.BTREE_FILE_FORMAT);
                VdbeAux.VdbeUsesBtree(v, iDb);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, minFormat, r2);
                j1 = VdbeAux.VdbeAddOp3(v, OPCode.OP_Ge, r2, 0, r1);
                VdbeAux.VdbeAddOp3(v, OPCode.OP_SetCookie, iDb, BtreeMeta.BTREE_FILE_FORMAT, r2);
                VdbeAux.VdbeJumpHere(v, j1);
                ExprHelper.ReleaseTempReg(pParse, r1);
                ExprHelper.ReleaseTempReg(pParse, r2);
            }
        }

        /*
    ** This function is called after an "ALTER TABLE ... ADD" statement
    ** has been parsed. Argument pColDef contains the text of the new
    ** column definition.
    **
    ** The Table structure pParse.pNewTable was extended to include
    ** the new column during parsing.
    */

        public static void AlterFinishAddColumn(Parse pParse, Token pColDef)
        {
            Table pNew; /* Copy of pParse.pNewTable */
            Table pTab; /* Table being altered */
            int iDb; /* Database number */
            string zDb; /* Database name */
            string zTab; /* Table name */
            string zCol; /* Null-terminated column definition */
            Column pCol; /* The new column */
            Expr pDflt; /* Default value for the new column */
            sqlite3 db; /* The database connection; */

            db = pParse.db;
            if (pParse.nErr != 0 /*|| db.mallocFailed != 0 */) return;
            pNew = pParse.pNewTable;
            Debug.Assert(pNew != null);
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(db));
            iDb = sqlite3SchemaToIndex(db, pNew.pSchema);
            zDb = db.aDb[iDb].zName;
            zTab = pNew.zName.Substring(16);
                // zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
            pCol = pNew.aCol[pNew.nCol - 1];
            pDflt = pCol.pDflt;
            pTab = Build.FindTable(db, zTab, zDb);
            Debug.Assert(pTab != null);

#if !SQLITE_OMIT_AUTHORIZATION
/* Invoke the authorization callback. */
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_ALTER_TABLE, zDb, pTab.zName, 0) ){
return;
}
#endif

            /* If the default value for the new column was specified with a
** literal NULL, then set pDflt to 0. This simplifies checking
** for an SQL NULL default below.
*/
            if (pDflt != null && pDflt.op == TokenKeyword.TK_NULL)
            {
                pDflt = null;
            }

            /* Check that the new column is not specified as PRIMARY KEY or UNIQUE.
      ** If there is a NOT NULL constraint, then the default value for the
      ** column must not be NULL.
      */
            if (pCol.isPrimKey != 0)
            {
                Utility.Sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
                return;
            }
            if (pNew.pIndex != null)
            {
                Utility.Sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
                return;
            }
            if ((db.flags & Flag.SQLITE_ForeignKeys) != 0 && pNew.pFKey != null && pDflt != null)
            {
                Utility.Sqlite3ErrorMsg(pParse,
                                "Cannot add a REFERENCES column with non-NULL default value");
                return;
            }
            if (pCol.notNull != 0 && pDflt == null)
            {
                Utility.Sqlite3ErrorMsg(pParse,
                                "Cannot add a NOT NULL column with default value NULL");
                return;
            }

            /* Ensure the default expression is something that sqlite3ValueFromExpr()
      ** can handle (i.e. not CURRENT_TIME etc.)
      */
            if (pDflt != null)
            {
                sqlite3_value pVal = null;
                if (sqlite3ValueFromExpr(db, pDflt, Const.SQLITE_UTF8, ColumnAffinityType.SQLITE_AFF_NONE, ref pVal) != 0)
                {
                    //        db.mallocFailed = 1;
                    return;
                }
                if (pVal == null)
                {
                    Utility.Sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default");
                    return;
                }
                sqlite3ValueFree(ref pVal);
            }

            /* Modify the CREATE TABLE statement. */
            zCol = pColDef.z.Substring(0, pColDef.n).Replace(";", " ").Trim();
                //sqlite3DbStrNDup(db, (char*)pColDef.z, pColDef.n);
            if (zCol != null)
            {
                //  char zEnd = zCol[pColDef.n-1];
                //      while( zEnd>zCol && (*zEnd==';' || SqliteInt.Sqlite3Isspace(*zEnd)) ){
                //    zEnd-- = '\0';
                //  }
                Build.NestedParse(pParse,
                                   "UPDATE \"%w\".%s SET " +
                                   "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " +
                                   "WHERE type = 'table' AND name = %Q",
                                   zDb, Helper.SchemaTable(iDb), pNew.addColOffset, zCol, pNew.addColOffset + 1,
                                   zTab
                    );
                MemPool.DbFree(db, ref zCol);
            }

            /* If the default value of the new column is NULL, then set the file
      ** format to 2. If the default value of the new column is not NULL,
      ** the file format becomes 3.
      */
            MinimumFileFormat(pParse, iDb, pDflt != null ? 3 : 2);

            /* Reload the schema of the modified table. */
            reloadTableSchema(pParse, pTab, pTab.zName);
        }

        /*
    ** This function is called by the parser after the table-name in
    ** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument
    ** pSrc is the full-name of the table being altered.
    **
    ** This routine makes a (partial) copy of the Table structure
    ** for the table being altered and sets Parse.pNewTable to point
    ** to it. Routines called by the parser as the column definition
    ** is parsed (i.e. Build.AddColumn()) add the new Column data to
    ** the copy. The copy of the Table structure is deleted by tokenize.c
    ** after parsing is finished.
    **
    ** Routine sqlite3AlterFinishAddColumn() will be called to complete
    ** coding the "ALTER TABLE ... ADD" statement.
    */

        public static void AlterBeginAddColumn(Parse pParse, SrcList pSrc)
        {
            Table pNew;
            Table pTab;
            Vdbe v;
            int iDb;
            int i;
            int nAlloc;
            sqlite3 db = pParse.db;

            /* Look up the table being altered. */
            Debug.Assert(pParse.pNewTable == null);
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(db));
            //      if ( db.mallocFailed != 0 ) goto exit_begin_add_column;
            pTab = Build.LocateTable(pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase);
            if (pTab == null) goto exit_begin_add_column;

            if (Utility.IsVirtual(pTab))
            {
                Utility.Sqlite3ErrorMsg(pParse, "virtual tables may not be altered");
                goto exit_begin_add_column;
            }

            /* Make sure this is not an attempt to ALTER a view. */
            if (pTab.pSelect != null)
            {
                Utility.Sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
                goto exit_begin_add_column;
            }

            Debug.Assert(pTab.addColOffset > 0);
            iDb = sqlite3SchemaToIndex(db, pTab.pSchema);

            /* Put a copy of the Table struct in Parse.pNewTable for the
      ** Build.AddColumn() function and friends to modify.  But modify
      ** the name by adding an "sqlite_altertab_" prefix.  By adding this
      ** prefix, we insure that the name will not collide with an existing
      ** table because user table are not allowed to have the "sqlite_"
      ** prefix on their name.
      */
            pNew = new Table(); // (Table*)Malloc.DbMallocZero( db, sizeof(Table))
            if (pNew == null) goto exit_begin_add_column;
            pParse.pNewTable = pNew;
            pNew.nRef = 1;
            pNew.dbMem = pTab.dbMem;
            pNew.nCol = pTab.nCol;
            Debug.Assert(pNew.nCol > 0);
            nAlloc = (((pNew.nCol - 1)/8)*8) + 8;
            Debug.Assert(nAlloc >= pNew.nCol && nAlloc%8 == 0 && nAlloc - pNew.nCol < 8);
            pNew.aCol = new Column[nAlloc]; // (Column*)Malloc.DbMallocZero( db, sizeof(Column) * nAlloc );
            pNew.zName = Print.MPrintf(db, "sqlite_altertab_%s", pTab.zName);
            if (pNew.aCol == null || pNew.zName == null)
            {
                //        db.mallocFailed = 1;
                goto exit_begin_add_column;
            }
            // memcpy( pNew.aCol, pTab.aCol, sizeof(Column) * pNew.nCol );
            for (i = 0; i < pNew.nCol; i++)
            {
                Column pCol = pTab.aCol[i].Copy();
                // sqlite3DbStrDup( db, pCol.zName );
                pCol.zColl = null;
                pCol.zType = null;
                pCol.pDflt = null;
                pCol.zDflt = null;
                pNew.aCol[i] = pCol;
            }
            pNew.pSchema = db.aDb[iDb].pSchema;
            pNew.addColOffset = pTab.addColOffset;
            pNew.nRef = 1;

            /* Begin a transaction and increment the schema cookie.  */
            Build.BeginWriteOperation(pParse, 0, iDb);
            v = SelectHelper.GetVdbe(pParse);
            if (v == null) goto exit_begin_add_column;
            Build.ChangeCookie(pParse, iDb);

            exit_begin_add_column:
            Build.SrcListDelete(db, ref pSrc);
            return;
        }
#endif
        // * ActionCode.SQLITE_ALTER_TABLE */
    }
}